﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
using System.Xml;

namespace HK.Reports
{
	/// <summary>
	/// Класс для формирования отчета
	/// </summary>
	public class Report
	{
		public functionService.prm_report p;
		public Font fontText;
        public Font fontGroupRow;
	 	public Font fontTableHeaders;
	 	public Font fontReportHeader;
	 	
	 	public PrintPreviewDialog preDialog;
		public PrintDocument document;

		public string reportHeader;
	 	public string reportFooter;

        /// <summary>
        /// Форматы данных столбцов
        /// </summary>
        public string[] repColsFormats;

        /// <summary>
        /// Наименования столбцов
        /// </summary>
		public string[] repColsName;

        /// <summary>
        /// Ширины столбцов в пикселах
        /// </summary>
        public int[] repColsWidth;
	 	
	 	public DataTable dt;
	 	public DataTableReader dtdr;
	
		public int numPage;
		public int nextY;
		public int nextX;

        public int pageHeight;
        public int pageWidth;
	 	
		/// <summary>
		/// Конструктор устанавливает параметры по умолчанию
		/// </summary>
		public Report(string nameReport)
		{
			this.p = new functionService.prm_report(nameReport);

			this.repColsWidth  = functionService.tagWork.readTAG_ToArrayInt(p.tk(1)); // Взять ширины столбцов
			this.repColsName = functionService.tagWork.readTAG_ToArray(p.tk(2)); // Взять наименования столбцов

            this.fontText = new Font("Arial", 10, System.Drawing.FontStyle.Regular); // Шрифт для текста
            this.fontGroupRow = new Font("Arial", 10, System.Drawing.FontStyle.Bold); // Шрифт для группированной строки
            this.fontTableHeaders = new Font("Arial", 10, System.Drawing.FontStyle.Bold); // Шрифт для заголовков столбцов
            this.fontReportHeader = new Font("Arial", 20, System.Drawing.FontStyle.Bold); // Шрифт для заголовка
	 		this.preDialog = new PrintPreviewDialog();
			this.document = new PrintDocument();
			this.dt = new DataTable();

			this.numPage = 1;
			this.nextY = 0;
			this.nextX = 0;
		}
		
		/// <summary>
		/// Конструктор устанавливает параметры для отчета
		/// </summary>
		/// <param name="nameReport">Имя отчета</param>
		/// <param name="dt">Таблицы с данными</param>
		/// <param name="headerReport">Текст заголовока отчета</param>
		/// <param name="footerReport">Текст итога отчета</param>
		/// <param name="colWidths">Ширины столбцов в пикселах</param>
		/// <param name="colCaptions">Заголовки столбцов в пикселах</param>
		public Report(string nameReport, DataTable dt, string headerReport, string footerReport, string colWidths, string colCaptions)
		{
            string colFormats = "";

			this.p = new functionService.prm_report("rep_" + nameReport);
			
			this.dt = dt; // Данные отчета
			this.reportHeader = headerReport; // Установить заголовок отчета
			this.reportFooter = footerReport; // Установить итог отчета
			
			this.repColsWidth  = functionService.tagWork.readTAG_ToArrayInt(colWidths); // Взять ширины столбцов
			this.repColsName = functionService.tagWork.readTAG_ToArray(colCaptions); // Взять наименования столбцов

            // Инициализация форматов по умолчанию
            for(int coli = 0; coli < dt.Columns.Count; coli++)
            {
                colFormats += "l;";                
            }
            colFormats = colFormats.Substring(0, colFormats.Length - 1);
            this.repColsFormats = colFormats.Split(';');
			
			this.fontText = new Font("Arial", 10, System.Drawing.FontStyle.Regular); // Шрифт для текста
            this.fontGroupRow = new Font("Arial", 10, System.Drawing.FontStyle.Bold); // Шрифт для группированной строки
	 		this.fontTableHeaders = new Font("Arial", 10, System.Drawing.FontStyle.Bold); // Шрифт для заголовков столбцов
	 		this.fontReportHeader = new Font("Arial", 14, System.Drawing.FontStyle.Bold); // Шрифт для заголовка
	 		this.preDialog = new PrintPreviewDialog();
			this.document = new PrintDocument();

			this.numPage = 1;
			this.nextY = 0;
			this.nextX = 0;
		}

        /// <summary>
        /// Конструктор устанавливает параметры для отчета
        /// </summary>
        /// <param name="nameReport">Имя отчета</param>
        /// <param name="dt">Таблицы с данными</param>
        /// <param name="headerReport">Текст заголовока отчета</param>
        /// <param name="footerReport">Текст итога отчета</param>
        /// <param name="colWidths">Ширины столбцов в пикселах</param>
        /// <param name="colCaptions">Заголовки столбцов в пикселах</param>
        /// <param name="colFormats">Формат столбцов таблицы l - по левому краю, c - по центру, r - по правому краю пример l;r;c;c;l;r</param>
        public Report(string nameReport, DataTable dt, string headerReport, string footerReport, string colWidths, string colCaptions, string colFormats)
            :this(nameReport, dt, headerReport, footerReport, colWidths, colCaptions)
        {
           this.repColsFormats = colFormats.Split(';');
        }

        /// <summary>
        /// Конструктор устанавливает параметры для отчета
        /// </summary>
        /// <param name="nameReport">Имя отчета</param>
        /// <param name="dt">Таблицы с данными</param>
        /// <param name="headerReport">Текст заголовока отчета</param>
        /// <param name="footerReport">Текст итога отчета</param>
        /// <param name="colWidths">Ширины столбцов в пикселах</param>
        /// <param name="colCaptions">Заголовки столбцов в пикселах</param>
        /// <param name="landscape">Формат бумаги: false - книжная, true - альбомная</param>
        public Report(string nameReport, DataTable dt, string headerReport, string footerReport, 
            string colWidths, string colCaptions, bool landscape)
            :this(nameReport, dt, headerReport, footerReport, colWidths, colCaptions)
        {
            this.document.DefaultPageSettings.Landscape = landscape;
        }

        /// <summary>
        /// Конструктор устанавливает параметры для отчета
        /// </summary>
        /// <param name="nameReport">Имя отчета</param>
        /// <param name="dt">Таблицы с данными</param>
        /// <param name="headerReport">Текст заголовока отчета</param>
        /// <param name="footerReport">Текст итога отчета</param>
        /// <param name="colWidths">Ширины столбцов в пикселах</param>
        /// <param name="colCaptions">Заголовки столбцов в пикселах</param>
        /// <param name="colFormats">Формат столбцов таблицы l - по левому краю, c - по центру, r - по правому краю пример l;r;c;c;l;r</param>
        /// <param name="landscape">Формат бумаги: false - книжная, true - альбомная</param>
        public Report(string nameReport, DataTable dt, string headerReport, string footerReport, 
            string colWidths, string colCaptions, string colFormats, bool landscape)
            : this(nameReport, dt, headerReport, footerReport, colWidths, colCaptions, colFormats)
        {
            this.document.DefaultPageSettings.Landscape = landscape;
        }

		/// <summary>
		/// Вывести форму с подготовленным отчетом
		/// </summary>
		public void ShowReport()
		{
            if (this.document.DefaultPageSettings.Landscape)
            {
                this.pageHeight = this.document.DefaultPageSettings.PaperSize.Width;
                this.pageWidth = this.document.DefaultPageSettings.PaperSize.Height;
            }
            else
            {
                this.pageHeight = this.document.DefaultPageSettings.PaperSize.Height;
                this.pageWidth = this.document.DefaultPageSettings.PaperSize.Width;
            }

			this.dtdr = this.dt.CreateDataReader();
	        this.document.PrintPage += new PrintPageEventHandler(this.Report_PrintPage);
			this.preDialog.Document = this.document;
			this.preDialog.ShowDialog();
		}
		
		/// <summary>
		/// Напечатать номер страницы
		/// </summary>
		/// <param name="e"></param>
		private void PrintNumPage(PrintPageEventArgs e)
		{
			string numPageStr;
			
			numPageStr = "стр: " + this.numPage.ToString();
			e.Graphics.DrawString(
				numPageStr, this.fontText, System.Drawing.Brushes.Black, 
				this.pageWidth - Convert.ToInt32(e.Graphics.MeasureString(numPageStr, this.fontText).Width + 20), 5);
		}
		
		/// <summary>
		/// Напечатать заголовок
		/// </summary>
		/// <param name="e"></param>
		private void PrintHeader(PrintPageEventArgs e)
		{
			int x;
			SizeF sizef;
			sizef = e.Graphics.MeasureString(this.reportHeader, this.fontReportHeader);
			
			x = this.pageWidth / 2 - Convert.ToInt32(sizef.Width / 2);
			e.Graphics.DrawString(this.reportHeader, this.fontReportHeader, System.Drawing.Brushes.Black, x, this.nextY);
			this.nextY += Convert.ToInt32(sizef.Height);
		}
		
		/// <summary>
		/// Напечатать итог
		/// </summary>
		/// <param name="e"></param>
		private void PrintFooter(PrintPageEventArgs e)
		{
			int x;
			SizeF sizef;
			sizef = e.Graphics.MeasureString(this.reportFooter, this.fontTableHeaders);
			
			x = 0;
			e.Graphics.DrawString(this.reportFooter, this.fontTableHeaders, System.Drawing.Brushes.Black, x, this.nextY);
			this.nextY += Convert.ToInt32(sizef.Height);			
		}
		
		/// <summary>
		/// Вывести ячейку
		/// </summary>
		/// <param name="e"></param>
		/// <param name="text"></param>
		/// <param name="fontText"></param>
		/// <param name="brush"></param>
		/// <param name="pen"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		private void DrawCell(PrintPageEventArgs e, string text, Font fontText, Brush brush, Pen pen, 
			float x, float y, float width, float height, string strFormat)
		{
            StringFormat format = new StringFormat();

            switch (strFormat)
            {
                case "r":
                    format.Alignment = StringAlignment.Far;
                    break;
                case "l":
                    format.Alignment = StringAlignment.Near;
                    break;
                case "c":
                    format.Alignment = StringAlignment.Center;
                    break;
                default:
                    format.Alignment = StringAlignment.Far;
                    break;

            };

			Rectangle rect = new Rectangle((int)x, (int)y, (int)width, (int)height);

			e.Graphics.DrawString(text, fontText, brush, rect, format);
			e.Graphics.DrawRectangle(pen, rect);
		}

        /// <summary>
        /// Вывести ячейку без границ
        /// </summary>
        /// <param name="e"></param>
        /// <param name="text"></param>
        /// <param name="fontText"></param>
        /// <param name="brush"></param>
        /// <param name="pen"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        private void DrawCellWhithOutBorder(PrintPageEventArgs e, string text, Font fontText, Brush brush,
            float x, float y, float width, float height)
        {
            Rectangle rect = new Rectangle((int)x, (int)y, (int)width, (int)height);
            e.Graphics.DrawString(text, fontText, brush, rect);
        }

		/// <summary>
		/// Напечатать таблицу
		/// </summary>
		/// <param name="e"></param>
		private void PrintDt(PrintPageEventArgs e)
		{
			e.HasMorePages = false;
			
			string text;
			int xtmp;
            int pageHeight = this.pageHeight - 50;

			int marginX = 30;
			int marginY = 20;
			this.nextX = marginX;
			this.nextY = marginY;
			
			PrintNumPage(e);
			
			if (this.numPage == 1) {
				PrintHeader(e);			
			}
			
			for (int coli=0; coli < this.dt.Columns.Count; coli++) {
				text = this.repColsName[coli];

				if (text.Length > this.repColsWidth[coli]){
					text = text.Substring(0, this.repColsWidth[coli]);
				}
				xtmp = this.repColsWidth[coli];
				DrawCell(
                    e, text, this.fontTableHeaders, Brushes.Black, Pens.Black, 
                    this.nextX, this.nextY, xtmp, this.fontTableHeaders.Height, 
                    this.repColsFormats[coli]);
				this.nextX += xtmp;
			}
			this.nextY += this.fontTableHeaders.Height;
			
			while (this.dtdr.Read()) {
				this.nextX = marginX;
				for (int coli=0; coli < this.dt.Columns.Count; coli++) {
					text = this.dtdr.GetValue(coli).ToString();
					if (text.Length > this.repColsWidth[coli]){
						text = text.Substring(0, this.repColsWidth[coli]);
					}
					xtmp = this.repColsWidth[coli];
                    DrawCell(e, text, this.fontText, Brushes.Black, Pens.Black, this.nextX, this.nextY, xtmp, this.fontText.Height, this.repColsFormats[coli]);
					this.nextX += xtmp;
				}
				this.nextY += this.fontText.Height;
				if (pageHeight <= this.nextY) {
					e.HasMorePages = true;
					this.numPage++;
					this.nextY = 0;
					return;
				}
			}
			
			this.PrintFooter(e);
			
			this.dtdr = this.dt.CreateDataReader();
			this.numPage = 1;
		}

        /// <summary>
        /// Напечатать группированную таблицу
        /// </summary>
        /// <param name="e"></param>
        private void PrintDtGroup(PrintPageEventArgs e)
        {
            e.HasMorePages = false;

            string text;
            int xtmp;
            int pageHeight = this.pageHeight - 50;

            int marginX = 30;
            int marginY = 20;
            this.nextX = marginX;
            this.nextY = marginY;

            PrintNumPage(e);

            if (this.numPage == 1)
            {
                PrintHeader(e);
            }

            for (int coli = 0; coli < this.dt.Columns.Count; coli++)
            {
                text = this.repColsName[coli];

                if (text.Length > this.repColsWidth[coli])
                {
                    text = text.Substring(0, this.repColsWidth[coli]);
                }

                // Не выводить заголовок если это сервисное поле
                if (this.dt.Columns[coli].ToString() == "RowType")
                {
                    xtmp = 0;
                }
                else
                {
                    xtmp = this.repColsWidth[coli];
                    DrawCell(e, text, this.fontTableHeaders, Brushes.Black, Pens.Black, this.nextX, this.nextY, xtmp, this.fontTableHeaders.Height, this.repColsFormats[coli]);
                }
                this.nextX += xtmp;
            }
            this.nextY += this.fontTableHeaders.Height;

            while (this.dtdr.Read())
            {
                Boolean grRow;

                // Опрделение является ли строка сгруппированной
                if (dtdr["RowType"].ToString() == "1"){
                    grRow = true;
                } else {
                    grRow = false;
                };

                this.nextX = marginX;
                for (int coli = 0; coli < this.dt.Columns.Count; coli++)
                {
                    text = this.dtdr.GetValue(coli).ToString();
                    if (text.Length > this.repColsWidth[coli])
                    {
                        text = text.Substring(0, this.repColsWidth[coli]);
                    }

                    // Если столбец сервисный то не выводить
                    Boolean isDraw = true;
                    if (dtdr.GetName(coli) == "RowType"){
                        isDraw = false;
                        xtmp = 0;
                    } else {
                        xtmp = this.repColsWidth[coli];
                    }

                    // Форматирование в зависимости от того группированная строка или нет
                    if (grRow) {
                        if (isDraw)
                            DrawCellWhithOutBorder(e, text, this.fontGroupRow, Brushes.Black, this.nextX, this.nextY, xtmp, this.fontText.Height);
                    } else {
                        if (isDraw)
                            DrawCell(e, text, this.fontText, Brushes.Black, Pens.Black, this.nextX, this.nextY, xtmp, this.fontText.Height, this.repColsFormats[coli]);
                    }

                    this.nextX += xtmp;
                }
                this.nextY += this.fontText.Height;
                if (pageHeight <= this.nextY)
                {
                    e.HasMorePages = true;
                    this.numPage++;
                    this.nextY = 0;
                    return;
                }
            }

            this.PrintFooter(e);

            this.dtdr = this.dt.CreateDataReader();
            this.numPage = 1;
        }
		/// <summary>
		/// Напечатать отчет
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Report_PrintPage(object sender, PrintPageEventArgs e)
		{
            Boolean gr = false;

            // Определение есть ли поле RowType
            for (int i = 0; i < this.dt.Columns.Count; i++)
            {
                if (this.dt.Columns[i].ToString() == "RowType")
                {
                    gr = true; // Если истина то таблица группированная
                    break;
                }
            }

            // Вызов нужного метода
            if (gr)
            {
                PrintDtGroup(e);
            }
            else
            {
                PrintDt(e);
            }
		}
	}
}
